/* eslint-disable no-unused-vars */
/* eslint-disable node/handle-callback-err */
const path = require('path')
const mkdirp = require('mkdirp')
const {
  Tapable,
  SyncHook,
  SyncBailHook,
  AsyncSeriesHook,
  AsyncParallelHook,
  AsyncSeriesBailHook
} = require('tapable')
const NormalModuleFactory = require('./NormalModuleFactory')
const Compilation = require('./Compilation')
const Stats = require('./Stats')
// Compiler -> 编译器
class Compiler extends Tapable {
  constructor (context) {
    super()
    this.context = context
    this.hooks = {
      done: new AsyncSeriesHook(['stats']), // 无论成功与否，一切已尘埃落定。
      beforeRun: new AsyncSeriesHook(['compiler']), // 运行前的准备活动，主要启用了文件读取的功能
      run: new AsyncSeriesHook(['compiler']), // “机器”已经跑起来了，在编译之前有缓存，则启用缓存，这样可以提高效率。
      thisCompilation: new SyncHook(['compilation', 'params']),
      compilation: new SyncHook(['compilation', 'params']),
      entryOption: new SyncBailHook(['context', 'entry']),
      beforeCompile: new AsyncSeriesHook(['params']), // 开始编译前的准备，创建的ModuleFactory，创建Compilation，并绑定ModuleFactory到Compilation上。同时处理一些不需要编译的模块，比如ExternalModule（远程模块）和DllModule（第三方模块）。
      compile: new SyncHook(['params']), // 编译了
      make: new AsyncParallelHook(['compilation']), // 从Compilation的addEntry函数，开始构建模块
      afterCompile: new AsyncSeriesHook(['compilation']), // 编译结束了
      emit: new AsyncSeriesHook(['compilation']) // 输出文件了
    }
  }

  emitAssets (compilation, callback) {
    // 当前需要做的核心： 01 创建dist   02 在目录创建完成之后执行文件的写操作

    // 01 定义一个工具方法用于执行文件的生成操作
    const emitFiles = err => {
      const assets = compilation.assets
      const outputPath = this.options.output.path

      for (const file in assets) {
        const source = assets[file]
        const targetPath = path.posix.join(outputPath, file)
        this.outputFileSystem.writeFileSync(targetPath, source, 'utf8')
      }

      callback(err)
    }

    // 02 创建目录之后启动文件写入
    this.hooks.emit.callAsync(compilation, err => {
      mkdirp.sync(this.options.output.path)
      emitFiles()
    })
  }

  run (callback) {
    console.log('run 方法执行了～～～')
    const finalCallback = (err, stats) => {
      callback(err, stats)
    }
    const onCompiled = (err, compilation) => {
      console.log('onCompiled 方法执行了～～～')
      // 最终在这里将处理好的 chunk 写入到指定的文件然后输出到 dist
      this.emitAssets(compilation, err => {
        const stats = new Stats(compilation)
        finalCallback(err, stats)
      })
    }

    //  beforeRun / run 钩子事件监听回调
    this.hooks.beforeRun.callAsync(this, (err) => {
      console.log('监听beforeRun')
      this.hooks.run.callAsync(this, err => {
        this.compile(onCompiled)
      })
    })
  }

  compile (callback) {
    const params = this.newCompilationParams() // 返回prams

    // 对钩子进行监听回调
    this.hooks.beforeRun.callAsync(params, err => {
      this.hooks.compile.call(params)
      const compilation = this.newCompilation(params)

      this.hooks.make.callAsync(compilation, err => {
        // console.log('make 钩子监听回调执行了～～～')
        // callback(err, compilation)
        compilation.seal(err => {
          this.hooks.afterCompile.callAsync(compilation, err => {
            callback(err, compilation)
          })
        })
      })
    })
  }

  newCompilationParams () {
    return {
      normalModuleFactory: new NormalModuleFactory()
    }
  }

  newCompilation (params) {
    const compilation = this.creatCompilation()
    this.hooks.thisCompilation.call(compilation, params)
    this.hooks.compilation.call(compilation, params)
    return compilation
  }

  creatCompilation () {
    return new Compilation(this)
  }
}
module.exports = Compiler
